热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

池子比率:BSV区块链上的去中心化金融应用——Uniswap分析

篇首语:本文由编程笔记#小编为大家整理,主要介绍了BSV 区块链上的 DeFi :Uniswap相关的知识,希望对你有一定的参考价值。 Uniswap Uniswap 是所谓的 Decentrali

篇首语:本文由编程笔记#小编为大家整理,主要介绍了BSV 区块链上的 DeFi :Uniswap相关的知识,希望对你有一定的参考价值。



Uniswap

Uniswap 是所谓的 Decentralized Exchange,它允许个人或称为流动性提供者,将 Token 汇集到智能合约中提供流动性。


概述

我们实现了 Uniswap V1,它只在 BSV 和 Token 之间直接交换。如@state 装饰器所示,我们使用一个带有 @state 装饰器 的有状态合约来表示池子。它包含两个 Token :一个用于我们正在交换的 Token (第 7 行),另一个是治理 Token (第 11 行),称为流动性池(LP) Token 。该池将 BSV 直接存储在 UTXO 中(以 satoshis 为单位),将 Token 存储在对应的公钥哈希下(第 3 行)。

contract Uniswap
// pool's public key
PubKey poolPubkey;
// the main token
@state
ERC20 token;
// the liquidity pool governance token
@state
ERC20 lpToken;
...



Uniswap 合约源代码

增加流动性

任何人都可以通过调用函数 addLiquidity 向池中添加流动性。有两种情况(在第 9 行检查):


  1. 首次增加流动性:可以存入任意数量的 BSV 和 Token 。
  2. 添加更多流动性: BSV 和 Token 存入的比率必须与池中的现有比率相匹配(第 22 行)。

// add bsv and token to liquidity pool
public function addLiquidity(PubKey sender, Sig senderSig, int tokenAmount, int senderBalance, int senderKeyIndex, int oldTokenBalance,
int lpSenderBalance, int lpSenderKeyIndex, int newBsvBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);

// mint new lp tokens for the liquidity provider
if (oldBsvBalance == 0)
// initialize pool

// initially, just mint new lp tokens per the amount of new bsvs deposited
int lpMint = newBsvBalance;
require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex));
else
// add more liquidity
int bsvAmount = newBsvBalance - oldBsvBalance;

// deposit ratio must be the same with current pool ration
// i.e., oldBsvBalance / oldTokenBalance == bsvAmount / tokenAmount
require(oldBsvBalance * tokenAmount == bsvAmount * oldTokenBalance);
// mint new lp tokens, proportinal to the amount of new bsvs deposited
int lpMint = this.lpToken.totalSupply() * bsvAmount / oldBsvBalance;
require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex));

// transfer tokens to the pool
require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex));

require(this.propagateState(newBsvBalance, txPreimage));

存入 BSV 后,新的 LP Token 在第 26 行按比例铸造给流动性提供者。 Token 在第 30 行转移到池子对应的账户。

例如,如果池中有 10 个 BSV 和 100 个 LP Token ,而 Alice 又向其中存入了 5 个 BSV ,则将向她铸造 50 个新的 LP Token 。


移除流动性

流动性提供者调用函数 removeLiquidity 来提取他们的资金,包括 BSV 和 Token 。

// remove bsv and token from liquidity pool
public function removeLiquidity(PubKey sender, int lpAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance,
int lpSenderBalance, int lpSenderKeyIndex, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);

// withdraw amount
int bsvAmount = oldBsvBalance * lpAmount / this.lpToken.totalSupply();
int tokenAmount = oldTokenBalance * lpAmount / this.lpToken.totalSupply();
// burn the lp tokens
require(this.lpToken.burn(sender, lpSenderBalance, lpAmount, lpSenderKeyIndex));
// transfer tokens from pool to the sender
require(this.token.transferFrom(this.poolPubkey, sender, tokenAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));
// transfer bsvs to the sender
int newBsvBalance = oldBsvBalance - bsvAmount;

require(this.propagateState(newBsvBalance, txPreimage));

流动性提供者拥有的 LP Token 的数量来提取流动性(第 9 行和第 10 行)。提款后,LP Token 在第 13 行被烧毁。第 16 行将 Token 从池子中转移到流动性提供者。第 18 行和第 20 行对 BSV 做同样的事情。

请注意,除了合约输出之外,还需要在同一交易中的另一个输出将 BSV 返回给流动性提供者。


BSV -> Token

用户调用函数 swapBsvToToken 将 BSV 兑换成 Token 。在池子收到 BSV 后(在第 7 行计算),第 10 行计算要返回的 Token 数量,第 13 行将它们返回给用户。

// swap bsvs for tokens
public function swapBsvToToken(PubKey sender, int tokenAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance,
int newBsvBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
int bsvAmount = newBsvBalance - oldBsvBalance;
// calculate tokens in return
int tokensAmount = this.getAmount(bsvAmount, oldBsvBalance, oldTokenBalance);
// transfer tokens from pool to the sender
require(this.token.transferFrom(this.poolPubkey, sender, tokensAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));
require(this.propagateState(newBsvBalance, txPreimage));


Token -> BSV

用户调用函数 swapTokenToBsv 将 Token 兑换成 BSV 。第 9 行计算要返回的 BSV 数量。第 13 行将 Token 转移到池子中。

// swap tokens for bsvs
public function swapTokenToBsv(PubKey sender, int tokenAmount, Sig senderSig, int senderBalance, int senderKeyIndex, int oldTokenBalance,
int lpSenderBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
// calculate bsvs in return
int bsvsAmount = this.getAmount(tokenAmount, oldTokenBalance, oldBsvBalance);
int newBsvBalance = oldBsvBalance - bsvsAmount;
// transfer tokens to the pool
require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex));

require(this.propagateState(newBsvBalance, txPreimage));

removeLiquidity 类似,需要另一个输出来将 BSV 返回给用户。


讨论

我们已经演示了如何在 BSV 区块链上实现一个基本的类似 Uniswap 的合约。有很多方法可以扩展它以使其更实用。


  • 价格公式:我们使用以下代码来确定价格,仅基于 BSV 和 Token 储备。它被称为恒定和公式,可能导致池子被排空。为了避免耗尽,可以使用更复杂的公式,如恒定乘积公式 (x * y = k),如 Uniswap 中。

// use reserve ratio as price
function getAmount(int input, int inputReserve, int outputReserve) : int
return outputReserve * input / inputReserve;


  • 流动性挖矿:我们可以对每次交换收取费用,并用这些费用来奖励流动性提供者。
  • 允许用户直接将一个 Token 换成另一个 Token 。

TokenSwap 实际上已经实现了以上所有以及更多。


致谢

本作品灵感来源于陈诚的这篇文章。


推荐阅读
  • 云函数与数据库API实现增删查改的对比
    本文将深入探讨使用云函数和数据库API实现数据操作(增删查改)的不同方法,通过详细的代码示例帮助读者更好地理解和掌握这些技术。文章不仅提供代码实现,还解释了每种方法的特点和适用场景。 ... [详细]
  • 深入解析Spring启动过程
    本文详细介绍了Spring框架的启动流程,帮助开发者理解其内部机制。通过具体示例和代码片段,解释了Bean定义、工厂类、读取器以及条件评估等关键概念,使读者能够更全面地掌握Spring的初始化过程。 ... [详细]
  • 深入解析动态代理模式:23种设计模式之三
    在设计模式中,动态代理模式是应用最为广泛的一种代理模式。它允许我们在运行时动态创建代理对象,并在调用方法时进行增强处理。本文将详细介绍动态代理的实现机制及其应用场景。 ... [详细]
  • 并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性
    Java并发编程实践目录并发编程01——ThreadLocal并发编程02——ConcurrentHashMap并发编程03——阻塞队列和生产者-消费者模式并发编程04——闭锁Co ... [详细]
  • Java项目分层架构设计与实践
    本文探讨了Java项目中应用分层的最佳实践,不仅介绍了常见的三层架构(Controller、Service、DAO),还深入分析了各层的职责划分及优化建议。通过合理的分层设计,可以提高代码的可维护性、扩展性和团队协作效率。 ... [详细]
  • 本文探讨了如何通过预处理器开关选择不同的类实现,并解决在特定情况下遇到的链接器错误。 ... [详细]
  • 本文介绍了如何在 Node.js 中使用 `setDefaultEncoding` 方法为可写流设置默认编码,并提供了详细的语法说明和示例代码。 ... [详细]
  • Redux入门指南
    本文介绍Redux的基本概念和工作原理,帮助初学者理解如何使用Redux管理应用程序的状态。Redux是一个用于JavaScript应用的状态管理库,特别适用于React项目。 ... [详细]
  • 深入解析Serverless架构模式
    本文将详细介绍Serverless架构模式的核心概念、工作原理及其优势。通过对比传统架构,探讨Serverless如何简化应用开发与运维流程,并介绍当前主流的Serverless平台。 ... [详细]
  • 本文介绍如何从字符串中移除大写、小写、特殊、数字和非数字字符,并提供了多种编程语言的实现示例。 ... [详细]
  • Linux环境下C语言实现定时向文件写入当前时间
    本文介绍如何在Linux系统中使用C语言编程,实现在每秒钟向指定文件中写入当前时间戳。通过此示例,读者可以了解基本的文件操作、时间处理以及循环控制。 ... [详细]
  • Python + Pytest 接口自动化测试中 Token 关联登录的实现方法
    本文将深入探讨 Python 和 Pytest 在接口自动化测试中如何实现 Token 关联登录,内容详尽、逻辑清晰,旨在帮助读者掌握这一关键技能。 ... [详细]
  • 由二叉树到贪心算法
    二叉树很重要树是数据结构中的重中之重,尤其以各类二叉树为学习的难点。单就面试而言,在 ... [详细]
  • 本文详细介绍了钩子(hook)的概念、原理及其在编程中的实际应用。通过对比回调函数和注册函数,解释了钩子的工作机制,并提供了具体的Python示例代码,帮助读者更好地理解和掌握这一重要编程工具。 ... [详细]
  • CSS高级技巧:动态高亮当前页面导航
    本文介绍了如何使用CSS实现网站导航栏中当前页面的高亮显示,提升用户体验。通过为每个页面的body元素添加特定ID,并结合导航项的类名,可以轻松实现这一功能。 ... [详细]
author-avatar
爱旅游的星琴
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有